package com.nageoffer.shortlink.admin.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.alibaba.fastjson2.JSON;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.LambdaUpdateWrapper;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.nageoffer.shortlink.admin.common.constant.RedisCacheConstant;
import com.nageoffer.shortlink.admin.common.convention.exception.ClientException;
import com.nageoffer.shortlink.admin.common.convention.exception.ServiceException;
import com.nageoffer.shortlink.admin.common.enums.UserErrorCode;
import com.nageoffer.shortlink.admin.mapper.UserMapper;
import com.nageoffer.shortlink.admin.pojo.dto.req.ShortLinkGroupSaveReqDTO;
import com.nageoffer.shortlink.admin.pojo.dto.req.UserLoginReqDTO;
import com.nageoffer.shortlink.admin.pojo.dto.req.UserRegisterReqDTO;
import com.nageoffer.shortlink.admin.pojo.dto.req.UserUpdateReqDTO;
import com.nageoffer.shortlink.admin.pojo.dto.resp.UserLoginRespDTO;
import com.nageoffer.shortlink.admin.pojo.dto.resp.UserRespDTO;
import com.nageoffer.shortlink.admin.pojo.entity.User;
import com.nageoffer.shortlink.admin.pojo.entity.UserInfo;
import com.nageoffer.shortlink.admin.service.GroupService;
import com.nageoffer.shortlink.admin.service.UserService;
import com.nageoffer.shortlink.admin.toolkit.JwtUtil;
import lombok.RequiredArgsConstructor;
import org.redisson.api.RBloomFilter;
import org.redisson.api.RLock;
import org.redisson.api.RedissonClient;
import org.springframework.beans.BeanUtils;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.TimeUnit;

@Service
@RequiredArgsConstructor
public class UserServiceImpl extends ServiceImpl<UserMapper, User> implements UserService {

    private final UserMapper userMapper;
    private final RBloomFilter<String> userRegisterCachePenetrationBloomFilter;
    private final RedissonClient redissonClient;
    private final StringRedisTemplate stringRedisTemplate;
    private final GroupService groupService;

    public UserRespDTO getUserByUsername(String username){
        LambdaQueryWrapper<User> queryWrapper = Wrappers.lambdaQuery(User.class).eq(User::getUsername,username);
        User user = userMapper.selectOne(queryWrapper);
        if(user == null){
            throw new ServiceException(UserErrorCode.USR_NOT_EXIST);
        }
        UserRespDTO userRespDTO = new UserRespDTO();
        BeanUtils.copyProperties(user,userRespDTO);
        return userRespDTO;
    }

    @Override
    public Boolean availableUername(String username) {
        return !userRegisterCachePenetrationBloomFilter.contains(username);
    }

    @Override
    @Transactional
    public void userRegister(UserRegisterReqDTO requestParam) {
        if(!availableUername(requestParam.getUsername()))
            throw new ClientException(UserErrorCode.USR_NAME_ALREADY_EXIST);
        RLock lock = redissonClient.getLock("");
        try{
            if(lock.tryLock()){
                int insertFlag = userMapper.insert(BeanUtil.toBean(requestParam,User.class));
                if(insertFlag < 1){
                    throw  new ClientException(UserErrorCode.USR_SAVE_FAILURE);
                }
                userRegisterCachePenetrationBloomFilter.add(requestParam.getUsername());
                groupService.saveGroup(requestParam.getUsername(),new ShortLinkGroupSaveReqDTO("默认分组"));
                return;
            }
            throw new ClientException(UserErrorCode.USR_NAME_ALREADY_EXIST);
        }finally {
            lock.unlock();
        }
    }

    @Override
    public void update(UserUpdateReqDTO requestParam) {
        LambdaUpdateWrapper<User> wrapper = Wrappers.lambdaUpdate(User.class)
                .eq(User::getUsername,requestParam.getUsername());
        userMapper.update(BeanUtil.toBean(requestParam,User.class),wrapper);
    }

    @Override
    public UserLoginRespDTO login(UserLoginReqDTO requestParam) {
        //通过用户名和密码、用户状态来查询是否存在
        LambdaQueryWrapper<User> wrapper = Wrappers.lambdaQuery(User.class)
                .eq(User::getUsername, requestParam.getUsername())
                .eq(User::getPassword, requestParam.getPassword())
                .eq(User::getDelFlag, 0);
        User user = userMapper.selectOne(wrapper);
        //用户不存在的处理情况
        if(user == null){
            throw new ClientException(UserErrorCode.USR_NOT_EXIST);
        }
        //TODO 判断用户是否登录
        String key = RedisCacheConstant.USER_LOGIN_KEY + user.getUsername();
        Boolean hasKey = stringRedisTemplate.hasKey(key);
        if(Boolean.TRUE.equals(hasKey)){
            throw new ClientException(UserErrorCode.USR_ALREADY_LOGIN);
        }

        //构造有效载荷
        Map<String, Object> claims = new HashMap<>();
        UserInfo userInfo = UserInfo.builder()
                .userId(user.getId())
                .username(user.getUsername())
                .realName(user.getRealName())
                .build();
        claims.put("userInfo", JSON.toJSONString(userInfo));
        //生成Jwt校验令牌
        String jwtToken = JwtUtil.generateToken(claims);
        //TODO 利用redission弄成原子操作
        stringRedisTemplate.opsForHash().put(key,jwtToken, JSON.toJSONString(user));
        stringRedisTemplate.expire(key,30L, TimeUnit.MINUTES);
        return new UserLoginRespDTO(jwtToken);
    }

    @Override
    public Boolean checkLogin(String username, String token) {
         return stringRedisTemplate.opsForHash().get(RedisCacheConstant.USER_LOGIN_KEY + username, token) != null;
    }

    @Override
    public void logout(String username, String token) {
        if(checkLogin(username,token)){
            stringRedisTemplate.delete(RedisCacheConstant.USER_LOGIN_KEY+username);
        }else
            throw new ClientException("用户token异常或者未登录");

    }


}
